﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using ADONETDataServicesExtension.Utils;

namespace ADONETDataServicesExtension.Parsing
{
    public class RequestQueryParser
    {
        internal const string FrameworkTypeName = "System.Data.Services.Parsing.RequestQueryParser";

        public static readonly ConstantExpression NullLiteral;

        static RequestQueryParser()
        {
            NullLiteral = ReflectionHelper.GetStaticField<ConstantExpression>(FrameworkTypeName, "NullLiteral");
        }

        public static IQueryable Where(IDataService service, IQueryable source, string predicate, RequestDescription description, out DoneTier dt)
        {
            LambdaExpression expression = ParseLambdaForWhere(service, source.ElementType, predicate, description, out dt);
            if (expression == null)
                return null;
            return source.Provider.CreateQuery(Expression.Call(typeof(Queryable), "Where", new Type[] { source.ElementType }, new Expression[] { source.Expression, Expression.Quote(expression) }));
        }

        private static LambdaExpression ParseLambdaForWhere(IDataService service, Type typeForIt, string expression, RequestDescription description, out DoneTier dt)
        {
            ParameterExpression parameterForIt = Expression.Parameter(typeForIt, "it");
            ExpressionParser parser = new ExpressionParser(service, parameterForIt, expression, description);
            Expression parserExpression = parser.ParseWhere(out dt);
            if (parserExpression == null)
                return null;
            return Expression.Lambda(parserExpression, new ParameterExpression[] { parameterForIt });
        }

        public static IQueryable OrderBy(IDataService service, IQueryable source, string ordering, RequestDescription description, out DoneTier dt)
        {
            ParameterExpression parameterForIt = Expression.Parameter(source.ElementType, "it");
            IEnumerable<DynamicOrdering> enumerable = new ExpressionParser(service, parameterForIt, ordering, description).ParseOrdering(out dt);
            Expression expression = source.Expression;
            string str = "OrderBy";
            string str2 = "OrderByDescending";
            foreach (DynamicOrdering ordering2 in enumerable.Where(e => e != null))
            {
                if (ordering2.Selector == null)
                    break;
                Type type = ordering2.Selector.Type;
                if (!service.Provider.GetTypeIsOrdered(type))
                    DataServiceException.CreateBadRequestError(Strings.RequestQueryParser_OrderByDoesNotSupportType(WebUtil.GetTypeName(service.Provider, ordering2.Selector.Type))).Throw();
                expression  = Expression.Call(typeof(Queryable), ordering2.Ascending ? str : str2, new Type[] { source.ElementType, type }, new Expression[] { expression, Expression.Quote(Expression.Lambda(ordering2.Selector, new ParameterExpression[] { parameterForIt })) });
                str = "ThenBy";
                str2 = "ThenByDescending";
            }
            return source.Provider.CreateQuery(expression);
        }

        public static bool IsNullConstant(Expression expression)
        {
            return ReflectionHelper.InvokeStaticMethod<bool>(FrameworkTypeName, "IsNullConstant", expression);
        }

        public static bool TypeAllowsNull(Type type)
        {
            return ReflectionHelper.InvokeStaticMethod<bool>(FrameworkTypeName, "TypeAllowsNull", type);
        }
    }
}
